﻿#include "GraphDataVTKPartSetManager.h"

#include "FITK_Kernel/FITKCore/FITKDataRepo.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsGeoCommand.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeometryMeshEntity.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKGeometryMeshVS.h"
#include "FITK_Interface/FITKInterfaceGeometry/FITKAbsVirtualTopoMapper.h"

#include <vtkUnstructuredGrid.h>
#include <vtkPolyData.h>
#include <vtkDoubleArray.h>
#include <vtkPolygon.h>
#include <vtkPointData.h>
#include <vtkPolyDataNormals.h>

#include <QDebug>

namespace Exchange
{
    //静态变量初始化
    GraphDataVTKPartSetManager* GraphDataVTKPartSetManager::_instance = nullptr;
    QMutex GraphDataVTKPartSetManager::m_mutex;

    GraphDataVTKPartSetInfo::GraphDataVTKPartSetInfo()
    {
        _pointGrid = vtkUnstructuredGrid::New();
        _pointGrid->SetPoints(vtkSmartPointer<vtkPoints>::New());
        _lineGrid = vtkUnstructuredGrid::New();
        _lineGrid->SetPoints(vtkSmartPointer<vtkPoints>::New());
        _faceGrid = vtkPolyData::New();
        _faceGrid->SetPoints(vtkSmartPointer<vtkPoints>::New());
        _faceGrid->SetPolys(vtkSmartPointer<vtkCellArray>::New());
        _solidGrid = vtkPolyData::New();
        _solidGrid->SetPoints(vtkSmartPointer<vtkPoints>::New());
        _solidGrid->SetPolys(vtkSmartPointer<vtkCellArray>::New());
    }

    GraphDataVTKPartSetInfo::~GraphDataVTKPartSetInfo()
    {
        if (_pointGrid) {
            _pointGrid->Delete();
            _pointGrid = nullptr;
        }

        if (_lineGrid) {
            _lineGrid->Delete();
            _lineGrid = nullptr;
        }

        if (_faceGrid) {
            _faceGrid->Delete();
            _faceGrid = nullptr;
        }
        if (_solidGrid) {
            _solidGrid->Delete();
            _solidGrid = nullptr;
        }
    }

    void GraphDataVTKPartSetInfo::clearData()
    {
        if (_pointGrid == nullptr || _lineGrid == nullptr || _faceGrid == nullptr)return;
        _pointGrid->Reset();
        _lineGrid->Reset();
        _faceGrid->Reset();
        _solidGrid->Reset();
        _point_VTKId_CellID.clear();
        _line_VTKId_CellID.clear();
        _face_VTKId_CellID.clear();
        _solid_VTKId_CellID.clear();
        _partPointHash.clear();
        _partLineHash.clear();
        _partFaceHash.clear();
    }

    void GraphDataVTKPartSetManager::updateGeoCommand(int geoCommandId)
    {
        Interface::FITKAbsGeoCommand* geoCommand = FITKDATAREPO->getTDataByID<Interface::FITKAbsGeoCommand>(geoCommandId);
        if (geoCommand == nullptr)return;
        Interface::FITKAbsGeoShapeAgent* shapeAgent = geoCommand->getShapeAgent();
        if (shapeAgent == nullptr)return;

        if (_geoCommandInfos.keys().contains(geoCommandId) == true) {
            _geoCommandInfos[geoCommandId]->clearData();
            //网格数据创建
            createPartPointGrid(_geoCommandInfos[geoCommandId]);
            createPartLineGrid(_geoCommandInfos[geoCommandId]);
            createPartFaceGrid(_geoCommandInfos[geoCommandId]);
            createPartSolidGrid(_geoCommandInfos[geoCommandId]);
        }
        else {
            //网格数据创建
            GraphDataVTKPartSetInfo* partInfo = new GraphDataVTKPartSetInfo();
            partInfo->_geoCommandID = geoCommandId;
            createPartPointGrid(partInfo);
            createPartLineGrid(partInfo);
            createPartFaceGrid(partInfo);
            createPartSolidGrid(partInfo);
            _geoCommandInfos.insert(geoCommandId, partInfo);
            //几何命令对象清除，管理中清除相关数据
            connect(geoCommand, &Interface::FITKAbsGeoCommand::dataObjectDestoried, [=]() {
                delete _geoCommandInfos[geoCommandId];
                _geoCommandInfos.remove(geoCommandId);
                });
        }
    }

    GraphDataVTKPartSetInfo* GraphDataVTKPartSetManager::getGeoCommandInfo(int geoCommandId)
    {
        return _geoCommandInfos.value(geoCommandId);
    }

    Interface::FITKGeoMeshVSPt* GraphDataVTKPartSetManager::getPartPointData(int virTopoId)
    {
        for (GraphDataVTKPartSetInfo* partInfo : _geoCommandInfos.values()) {
            if (partInfo == nullptr)continue;
            //判断当前part信息中是否包含该虚拓扑
            if (partInfo->_partPointHash.keys().contains(virTopoId) == false)continue;
            return partInfo->_partPointHash.value(virTopoId);
        }
        return nullptr;
    }

    Interface::FITKGeoMeshVSEdgeEntity* GraphDataVTKPartSetManager::getPartLineData(int virTopoId)
    {
        for (GraphDataVTKPartSetInfo* partInfo : _geoCommandInfos.values()) {
            if (partInfo == nullptr)continue;
            //判断当前part信息中是否包含该虚拓扑
            if (partInfo->_partLineHash.keys().contains(virTopoId) == false)continue;
            return partInfo->_partLineHash.value(virTopoId);
        }
        return nullptr;
    }

    Interface::FITKGeoMeshVSFaceEntity* GraphDataVTKPartSetManager::getPartFaceData(int virTopoId)
    {
        for (GraphDataVTKPartSetInfo* partInfo : _geoCommandInfos.values()) {
            if (partInfo == nullptr)continue;
            //判断当前part信息中是否包含该虚拓扑
            if (partInfo->_partFaceHash.keys().contains(virTopoId) == false)continue;
            return partInfo->_partFaceHash.value(virTopoId);
        }
        return nullptr;
    }
    void Exchange::GraphDataVTKPartSetManager::initialize()
    {
        return;
    }

    void GraphDataVTKPartSetManager::finalize()
    {
        return;
    }
    void GraphDataVTKPartSetManager::createPartPointGrid(GraphDataVTKPartSetInfo* partInfo)
    {
        if (partInfo == nullptr)return;
        Interface::FITKAbsGeoCommand* geoCommand = FITKDATAREPO->getTDataByID<Interface::FITKAbsGeoCommand>(partInfo->_geoCommandID);
        if (geoCommand == nullptr)return;
        Interface::FITKAbsGeoShapeAgent* shapeAgent = geoCommand->getShapeAgent();
        if (shapeAgent == nullptr)return;
        Interface::FITKGeometryMeshVS* triData = shapeAgent->getMeshVS();
        if (triData == nullptr)return;

        // 获取虚拓扑管理器
        Interface::FITKVirtualTopoManager* topoMgr = shapeAgent->getVirtualTopoManager();
        if (!topoMgr) return;
        Interface::FITKShapeVirtualTopoManager* vsMgrPoint = topoMgr->getShapeVirtualTopoManager(Interface::FITKGeoEnum::VTopoShapeType::VSPoint);
        if (!vsMgrPoint) return;

        const QHash<int, Interface::FITKGeoMeshVSPt*> pointsHash = triData->getPointItems();
        vtkUnstructuredGrid* pointGrid = partInfo->_pointGrid;
        if (pointGrid == nullptr)return;
        vtkPoints* points = pointGrid->GetPoints();

        int nVerts = vsMgrPoint->getDataCount();
        for (int i = 0; i < nVerts; i++) {
            Interface::FITKAbsVirtualTopo* vTopoV = vsMgrPoint->getDataByIndex(i);
            if (!vTopoV) continue;
            if (!vTopoV->isEnable()) continue;
            int pointId = vTopoV->getDataObjectID();
            Interface::FITKGeoMeshVSPt* pt = pointsHash.value(pointId);
            if (!pt) continue;

            int pointVTKID = points->InsertNextPoint(pt->x(), pt->y(), pt->z());
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            cell->InsertNextId(pointVTKID);
            int vtkid = pointGrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);
            partInfo->_point_VTKId_CellID.insert(vtkid, pointId);
            partInfo->_partPointHash.insert(pointId, pt);

            // 记录虚拓扑ID与索引
            partInfo->_pointVirTopoIdToIndex.insert(pointId, i);
        }
    }

    void GraphDataVTKPartSetManager::createPartLineGrid(GraphDataVTKPartSetInfo* partInfo)
    {
        if (partInfo == nullptr)return;
        Interface::FITKAbsGeoCommand* geoCommand = FITKDATAREPO->getTDataByID<Interface::FITKAbsGeoCommand>(partInfo->_geoCommandID);
        if (geoCommand == nullptr)return;
        Interface::FITKAbsGeoShapeAgent* shapeAgent = geoCommand->getShapeAgent();
        if (shapeAgent == nullptr)return;
        Interface::FITKGeometryMeshVS* triData = shapeAgent->getMeshVS();
        if (triData == nullptr)return;

        Interface::FITKVirtualTopoManager* topoMgr = shapeAgent->getVirtualTopoManager();
        if (!topoMgr) return;
        Interface::FITKShapeVirtualTopoManager* vsMgrEdge = topoMgr->getShapeVirtualTopoManager(Interface::FITKGeoEnum::VTopoShapeType::VSEdge);
        if (!vsMgrEdge) return;

        QHash<int, Interface::FITKGeoMeshVSEdgeEntity*> lineHash = triData->getEdgeItems();
        vtkUnstructuredGrid* lineGrid = partInfo->_lineGrid;
        if (lineGrid == nullptr)return;
        vtkPoints* points = lineGrid->GetPoints();

        int nEdges = vsMgrEdge->getDataCount();
        for (int i = 0; i < nEdges; i++) {
            Interface::FITKAbsVirtualTopo* vTopoE = vsMgrEdge->getDataByIndex(i);
            if (!vTopoE) continue;
            if (!vTopoE->isEnable()) continue;
            int edgeId = vTopoE->getDataObjectID();
            Interface::FITKGeoMeshVSEdgeEntity* edge = lineHash.value(edgeId);
            if (!edge) continue;

            const QList<Interface::FITKGeoMeshVSPt*>& pts = edge->getPoints();
            vtkSmartPointer<vtkIdList> polyLineCell = vtkSmartPointer<vtkIdList>::New();
            for (Interface::FITKGeoMeshVSPt* pt : pts) {
                int ptIndex = points->InsertNextPoint(pt->x(), pt->y(), pt->z());
                polyLineCell->InsertNextId(ptIndex);
            }
            int vtkID = lineGrid->InsertNextCell(VTKCellType::VTK_POLY_LINE, polyLineCell);
            partInfo->_line_VTKId_CellID.insert(vtkID, edgeId);
            partInfo->_partLineHash.insert(edgeId, edge);

            // 记录虚拓扑ID与索引
            partInfo->_lineVirTopoIdToIndex.insert(edgeId, i);
        }
    }

    void GraphDataVTKPartSetManager::createPartFaceGrid(GraphDataVTKPartSetInfo* partInfo)
    {
        if (partInfo == nullptr)return;
        Interface::FITKAbsGeoCommand* geoCommand = FITKDATAREPO->getTDataByID<Interface::FITKAbsGeoCommand>(partInfo->_geoCommandID);
        if (geoCommand == nullptr)return;
        Interface::FITKAbsGeoShapeAgent* shapeAgent = geoCommand->getShapeAgent();
        if (shapeAgent == nullptr)return;
        Interface::FITKGeometryMeshVS* triData = shapeAgent->getMeshVS();
        if (triData == nullptr)return;

        Interface::FITKVirtualTopoManager* topoMgr = shapeAgent->getVirtualTopoManager();
        if (!topoMgr) return;
        Interface::FITKShapeVirtualTopoManager* vsMgrFace = topoMgr->getShapeVirtualTopoManager(Interface::FITKGeoEnum::VTopoShapeType::VSFace);
        if (!vsMgrFace) return;

        const QHash<int, Interface::FITKGeoMeshVSFaceEntity*> facesHash = triData->getFaceItems();
        vtkPolyData* faceGrid = partInfo->_faceGrid;
        if (faceGrid == nullptr)return;
        vtkPoints* points = faceGrid->GetPoints();

        int ptIndexStart = 0;
        int nFaces = vsMgrFace->getDataCount();
        for (int i = 0; i < nFaces; i++) {
            Interface::FITKAbsVirtualTopo* vTopoF = vsMgrFace->getDataByIndex(i);
            if (!vTopoF) continue;
            if (!vTopoF->isEnable()) continue;
            int faceId = vTopoF->getDataObjectID();
            Interface::FITKGeoMeshVSFaceEntity* face = facesHash.value(faceId);
            if (!face) continue;

            const QList<Interface::FITKGeoMeshVSPt*>& facePoints = face->getPoints();
            for (Interface::FITKGeoMeshVSPt* facePoint : facePoints) {
                points->InsertNextPoint(facePoint->x(), facePoint->y(), facePoint->z());
            }
            const QList<Interface::FITKGeoMeshVSTri*>& tris = face->getTriangles();
            for (Interface::FITKGeoMeshVSTri* tri : tris) {
                const int* nodes = tri->getNode();
                int ptIndex1 = ptIndexStart + nodes[0];
                int ptIndex2 = ptIndexStart + nodes[1];
                int ptIndex3 = ptIndexStart + nodes[2];
                vtkSmartPointer<vtkIdList> triCell = vtkSmartPointer<vtkIdList>::New();
                triCell->InsertNextId(ptIndex1);
                triCell->InsertNextId(ptIndex2);
                triCell->InsertNextId(ptIndex3);
                int vtkID = faceGrid->InsertNextCell(VTKCellType::VTK_TRIANGLE, triCell);
                partInfo->_face_VTKId_CellID.insert(vtkID, faceId);
            }
            partInfo->_partFaceHash.insert(faceId, face);

            // 记录虚拓扑ID与索引
            partInfo->_faceVirTopoIdToIndex.insert(faceId, i);

            ptIndexStart += facePoints.count();
        }
        calculatePointNormals(faceGrid);
    }

    void GraphDataVTKPartSetManager::createPartSolidGrid(GraphDataVTKPartSetInfo* partInfo)
    {
        if (partInfo == nullptr) return;
        Interface::FITKAbsGeoCommand* geoCommand = FITKDATAREPO->getTDataByID<Interface::FITKAbsGeoCommand>(partInfo->_geoCommandID);
        if (geoCommand == nullptr) return;
        Interface::FITKAbsGeoShapeAgent* shapeAgent = geoCommand->getShapeAgent();
        if (shapeAgent == nullptr) return;
        Interface::FITKGeometryMeshVS* triData = shapeAgent->getMeshVS();
        if (triData == nullptr) return;

        Interface::FITKVirtualTopoManager* topoMgr = shapeAgent->getVirtualTopoManager();
        if (!topoMgr) return;
        Interface::FITKShapeVirtualTopoManager* vsMgrSolid = topoMgr->getShapeVirtualTopoManager(Interface::FITKGeoEnum::VTopoShapeType::VSSolid);
        if (!vsMgrSolid) return;

        // 获取所有面数据用于构建体
        const QHash<int, Interface::FITKGeoMeshVSFaceEntity*> facesHash = triData->getFaceItems();
        vtkPolyData* solidGrid = partInfo->_solidGrid;
        if (solidGrid == nullptr) return;
        vtkPoints* points = solidGrid->GetPoints();

        int ptIndexStart = 0;
        int nSolids = vsMgrSolid->getDataCount();
        for (int i = 0; i < nSolids; i++) {
            Interface::FITKAbsVirtualTopo* vTopoS = vsMgrSolid->getDataByIndex(i);
            if (!vTopoS) continue;
            if (!vTopoS->isEnable()) continue;

            int solidId = vTopoS->getDataObjectID();

            // 遍历实体所有的面
            Interface::FITKVirtualTopoMapper vMapper;
            vMapper.mapTopo(vTopoS, Interface::FITKGeoEnum::VTopoShapeType::VSFace);

            int nFaces = vMapper.length();
            for (int j = 0; j < nFaces; j++) {
                Interface::FITKAbsVirtualTopo* vTopoF = vMapper.virtualTopo(j);
                if (!vTopoF) continue;
                if (!vTopoF->isEnable()) continue;

                int faceId = vTopoF->getDataObjectID();
                Interface::FITKGeoMeshVSFaceEntity* face = facesHash.value(faceId);
                if (!face) continue;

                // 添加面的所有点
                const QList<Interface::FITKGeoMeshVSPt*>& facePoints = face->getPoints();
                for (Interface::FITKGeoMeshVSPt* facePoint : facePoints) {
                    points->InsertNextPoint(facePoint->x(), facePoint->y(), facePoint->z());
                }

                // 添加面的所有三角形
                const QList<Interface::FITKGeoMeshVSTri*>& tris = face->getTriangles();
                for (Interface::FITKGeoMeshVSTri* tri : tris) {
                    const int* nodes = tri->getNode();
                    int ptIndex1 = ptIndexStart + nodes[0];
                    int ptIndex2 = ptIndexStart + nodes[1];
                    int ptIndex3 = ptIndexStart + nodes[2];

                    vtkSmartPointer<vtkIdList> triCell = vtkSmartPointer<vtkIdList>::New();
                    triCell->InsertNextId(ptIndex1);
                    triCell->InsertNextId(ptIndex2);
                    triCell->InsertNextId(ptIndex3);
                    int vtkID = solidGrid->InsertNextCell(VTKCellType::VTK_TRIANGLE, triCell);

                    // 记录体ID与VTK单元ID的映射
                    partInfo->_solid_VTKId_CellID.insert(vtkID, solidId);
                }

                ptIndexStart += facePoints.count();
            }

            // 记录虚拓扑ID与索引的映射
            partInfo->_solidVirTopoIdToIndex.insert(solidId, i);
        }

        // 计算法向量
        calculatePointNormals(solidGrid);
    }

    void GraphDataVTKPartSetManager::calculatePointNormals(vtkPolyData* polyData)
    {
        if (!polyData)
        {
            return;
        }

        // For computing points nromals.
        //@{
        vtkSmartPointer<vtkPoints> tempPts = vtkSmartPointer<vtkPoints>::New();
        //@}

        int nPts = polyData->GetNumberOfPoints();

        vtkSmartPointer<vtkDoubleArray> pointsNormals = vtkSmartPointer<vtkDoubleArray>::New();
        pointsNormals->SetNumberOfComponents(3);
        pointsNormals->SetNumberOfTuples(nPts);
        pointsNormals->FillComponent(0, 0);
        pointsNormals->FillComponent(1, 0);
        pointsNormals->FillComponent(2, 0);

        int cellId = 0;
        vtkIdType npts = 0;
#if VTK_MAJOR_VERSION < 9
        vtkIdType* pts = nullptr;
#else
        const vtkIdType* pts = nullptr;
#endif

        // Add two double tuple 3.
        auto addDouble3 = [&](double* v1, double* v2, double* result)
            {
                for (int i = 0; i < 3; i++)
                {
                    result[i] = v1[i] + v2[i];
                }
            };

        vtkPoints* points = polyData->GetPoints();

        double result[3]{ 0., 0., 0. };
        for (cellId = 0, polyData->GetPolys()->InitTraversal(); polyData->GetPolys()->GetNextCell(npts, pts); ++cellId)
        {
            tempPts->Reset();
            tempPts->SetNumberOfPoints(0);
            for (vtkIdType i = 0; i < npts; ++i)
            {
                tempPts->InsertNextPoint(points->GetPoint(pts[i]));
            }

            double norCellCurrent[3];
            vtkPolygon::ComputeNormal(tempPts, norCellCurrent);

            for (vtkIdType i = 0; i < npts; ++i)
            {
                int iPt = pts[i];
                double* norPtCurrent = pointsNormals->GetTuple3(iPt);

                // Add normals.
                addDouble3(norPtCurrent, norCellCurrent, result);
                pointsNormals->SetTuple3(iPt, result[0], result[1], result[2]);
            }
        }

        double flipDirection = 1.0;
        for (vtkIdType i = 0; i < nPts; ++i)
        {
            double* ptNormal = pointsNormals->GetTuple3(i);
            const double length = sqrt(pow(ptNormal[0], 2) + pow(ptNormal[1], 2) + pow(ptNormal[2], 2)) * flipDirection;
            if (!qFuzzyCompare(length, 0.))
            {
                ptNormal[0] /= length;
                ptNormal[1] /= length;
                ptNormal[2] /= length;
            }

            pointsNormals->SetTuple3(i, ptNormal[0], ptNormal[1], ptNormal[2]);

            qDebug() << ptNormal[0] << ptNormal[1] << ptNormal[2];
        }

        polyData->GetPointData()->SetNormals(pointsNormals);
    }
}
